﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.IO;
using MvcEngine.Core;
using System.Reflection;
using System.Linq.Expressions;
using MvcEngine.Core.Validation;
using System.Web.Routing;
using System.Globalization;
using System.Web;
using MvcEngine.Core.Helpers;
using MvcEngine.Mvc.Extensions;

namespace MvcEngine.Mvc.UI
{
    public class FormView<TModel>
        where TModel : class
    {
        private readonly TextWriter writer;
        private readonly ViewContext context;
        private IDictionary<string, object> htmlAttributes = new Dictionary<string, object>();
        private readonly List<FormViewItem> items = new List<FormViewItem>();
        private Type modelType = null;

        public TModel ModelItem { get; private set; }

        public FormView(TModel modelItem, TextWriter writer, ViewContext context)
        {
            this.writer = writer;
            this.context = context;
            this.ModelItem = modelItem;
            this.modelType = typeof(TModel);
        }

        public FormView<TModel> Items(Action<FormView<TModel>> item)
        {
            item(this);
            return this;
        }

        public FormViewItem<TItemModel> For<TItemModel>(Expression<Func<TModel, TItemModel>> propertySpecifier)
        {
            Expression expression = RemoveUnary(propertySpecifier.Body);
            MemberExpression memberExpression = expression as MemberExpression;

            string inferredName = memberExpression.Member.Name;

            PropertyInfo property = memberExpression.Member as PropertyInfo;

            TItemModel value = propertySpecifier.Compile()(ModelItem);

            var item = new FormViewItem<TItemModel>(value, property.PropertyType, inferredName);
            items.Add(item);
            return item;
        }

        public FormViewItem<TItemModel> For<TItemModel>(string name)
        {
            var item = new FormViewItem<TItemModel>(default(TItemModel), typeof(TItemModel), name);
            items.Add(item);
            return item;
        }

        public FormView<TModel> SetAttributes(object attributes)
        {
            this.htmlAttributes = ((IDictionary<string, object>)new RouteValueDictionary(attributes));
            return this;
        }

        public IDictionary<string, object> Attributes
        {
            get { return htmlAttributes; }
        }

        private static Expression RemoveUnary(Expression body)
        {
            var unary = body as UnaryExpression;
            if (unary != null)
            {
                return unary.Operand;
            }
            return body;
        }

        public static string ExpressionToName(MemberExpression memberExpression)
        {
            return memberExpression == null ? null : memberExpression.Member.Name;
        }

        public override string ToString()
        {
            writer.Write("<div");
            StringBuilder builder = new StringBuilder();
            foreach (var pair in this.Attributes)
            {
                string key = pair.Key;
                string str2 = HttpUtility.HtmlAttributeEncode(pair.Value.ToString());
                builder.AppendFormat(CultureInfo.InvariantCulture, " {0}=\"{1}\"", new object[] { key, str2 });
            }
            writer.Write(builder.ToString());
            writer.Write(">");

            foreach (var element in items)
            {
                ViewDataDictionary viewData = new ViewDataDictionary(element);
                viewData.ModelState.CopyFrom(context.ViewData.ModelState);
                ViewEngineResult result = null;

                if (!string.IsNullOrEmpty(element.TemplateControl))
                    result = ViewEngines.Engines.FindPartialView(context, element.TemplateControl);
                else
                    result = ViewEngines.Engines.FindPartialView(context, element.Type.Name);

                result.View.Render(new ViewContext(context, result.View, viewData, context.TempData, writer), writer);
            }
            writer.Write("</div>");
            return string.Empty;
        }
    }
}
